package initConfig

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

var (
	log  *zap.Logger
	once sync.Once
)

// InitLogger 初始化并返回 zap.Logger 实例
func InitLogger(logConfig LogConfig) *zap.Logger {
	// 获取当前工作目录
	workDir, err := os.Getwd()
	if err != nil {
		fmt.Printf("无法获取工作目录: %v\n", err)
		os.Exit(1) // 终止程序
	}

	// 定义日志目录路径，基于工作目录
	logDir := filepath.Join(workDir, "logs")

	if err := os.MkdirAll(logDir, os.ModePerm); err != nil {
		fmt.Printf("无法创建日志目录: %v\n", err)
		os.Exit(1) // 终止程序
	}

	// 初始化日志记录器
	log, err := SetupLogger(logConfig)
	if err != nil {
		fmt.Printf("初始化logger失败: %v\n", err)
		os.Exit(1) // 终止程序
	}

	// 记录启动信息
	zap.L().Info("应用启动")
	err = log.Sync()
	if err != nil {
		zap.L().Info("刷新日志缓冲区失败", zap.Error(err))
	}

	return log
}

// SetupLogger 初始化日志记录器
func SetupLogger(logConfig LogConfig) (*zap.Logger, error) {
	var err error

	// 使用 sync.Once 确保 InitLogger 的核心初始化逻辑只会执行一次
	// 避免多次调用 InitLogger 导致重复初始化日志记录器
	// zap.Logger 是全局可用的日志记录器，通常只需要初始化一次
	// 如果多次初始化，可能会导致资源浪费或冲突
	once.Do(func() {
		// 根据解析后的配置信息，创建一个 zapcore.Core 对象。
		// zapcore.Core 是 zap 日志库的核心组件，负责定义日志的编码方式、输出目标和日志级别过滤规则
		core := newZapCore(logConfig)

		// 使用 zap.New 创建一个新的 zap.Logger 实例。
		// 配置以下选项：
		// zap.AddCaller()：在日志中记录调用者的文件名和行号（例如 "main.go:20"）。
		log = zap.New(core, zap.AddCaller())

		// 将新创建的 zap.Logger 实例设置为全局日志记录器
		// 在程序的任何地方都可以直接使用 zap.L().Info(...) 或 zap.S().Error(...) 记录日志，无需显式传递 zap.Logger 实例
		zap.ReplaceGlobals(log)
	})
	return log, err
}

// newZapCore 创建日志核心
func newZapCore(logConfig LogConfig) zapcore.Core {
	// 获取日志级别
	level := getLogLevel(logConfig.Level)

	// 获取编码器
	encoder := getEncoder(logConfig.Format, logConfig.TimeFormat)

	// 获取文件输出同步器
	fileSyncer := getLogWriter(
		logConfig.LogFile,
		logConfig.MaxSize,
		logConfig.MaxAge,
		logConfig.MaxBackups,
		logConfig.Compress,
	)

	// 获取终端输出同步器
	consoleSyncer := zapcore.AddSync(os.Stdout)

	// 使用 zapcore.NewTee 同时输出到文件和终端
	core := zapcore.NewTee(
		zapcore.NewCore(encoder, fileSyncer, level),    // 输出到文件
		zapcore.NewCore(encoder, consoleSyncer, level), // 输出到终端
	)

	return core
}

// getLogLevel 获取日志级别
func getLogLevel(levelStr string) zapcore.Level {
	switch levelStr {
	case "debug":
		return zap.DebugLevel
	case "info":
		return zap.InfoLevel
	case "warn":
		return zap.WarnLevel
	case "error":
		return zap.ErrorLevel
	case "fatal":
		return zap.FatalLevel
	default:
		return zap.InfoLevel
	}
}

// getEncoder 获取编码器
func getEncoder(format, timeFormat string) zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format(timeFormat))
	}
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	if format == "console" {
		return zapcore.NewConsoleEncoder(encoderConfig)
	}
	return zapcore.NewJSONEncoder(encoderConfig)
}

// getLogWriter 获取日志输出同步器
func getLogWriter(filename string, maxSize, maxAge, maxBackups int, compress bool) zapcore.WriteSyncer {
	// 转换为系统特定的路径格式
	cleanPath := filepath.Clean(strings.ReplaceAll(filename, "\"", ""))

	absPath, err := filepath.Abs(cleanPath)
	if err != nil {
		fmt.Printf("无法获取日志文件的绝对路径: %v\n", err)
	} else {
		fmt.Printf("日志文件绝对路径: %s\n", absPath)
	}

	lumberJackLogger := &lumberjack.Logger{
		Filename:   cleanPath, // 使用清理后的路径
		MaxSize:    maxSize,
		MaxAge:     maxAge,
		MaxBackups: maxBackups,
		Compress:   compress,
	}
	return zapcore.AddSync(lumberJackLogger)
}
